import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_squared_error
from scipy.optimize import minimize  # 用于纳什均衡计算


# 2. 模拟数据生成
def generate_competitive_data(n_samples=1000, n_competitors=3):
    """生成包含本产品和竞品信息的模拟数据"""
    np.random.seed(42)

    # 基础特征
    data = {
        'base_price': np.random.uniform(10, 100, n_samples),
        'quality': np.random.normal(0.5, 0.1, n_samples),
        'marketing': np.random.poisson(3, n_samples),
        'weekday': np.random.randint(0, 7, n_samples)
    }

    # 竞品特征
    for i in range(n_competitors):
        data[f'comp_{i}_price'] = data['base_price'] * np.random.uniform(0.8, 1.2, n_samples)
        data[f'comp_{i}_promo'] = np.random.binomial(1, 0.2, n_samples)

    # 目标变量(本产品销量)
    noise = np.random.normal(0, 5, n_samples)
    data['sales'] = (
            100
            - 0.8 * data['base_price']
            + 15 * data['quality']
            + 5 * data['marketing']
            - 0.5 * sum(data[f'comp_{i}_price'] for i in range(n_competitors))
            + 10 * sum(data[f'comp_{i}_promo'] for i in range(n_competitors))
            + noise
    )

    return pd.DataFrame(data)


# 生成数据
df = generate_competitive_data()


# 3. 特征工程
def create_features(df):
    """创建竞品博弈相关特征"""
    # 价格差异特征
    df['price_diff_avg'] = df[[f'comp_{i}_price' for i in range(3)]].sub(df['base_price'], axis=0).mean(axis=1)

    # 竞品促销活动计数
    df['competitor_promo_count'] = df[[f'comp_{i}_promo' for i in range(3)]].sum(axis=1)

    # 相对质量指标(假设)
    df['relative_quality'] = df['quality'] / (1 + df[[f'comp_{i}_price' for i in range(3)]].mean(axis=1) / 100)

    # 价格百分位特征
    all_prices = ['base_price'] + [f'comp_{i}_price' for i in range(3)]
    df['price_rank'] = df[all_prices].rank(axis=1)['base_price']

    return df


df = create_features(df)

# 4. 构建基础LightGBM模型
# 划分特征和目标变量
features = ['base_price', 'quality', 'marketing', 'weekday',
            'price_diff_avg', 'competitor_promo_count',
            'relative_quality', 'price_rank']
target = 'sales'

X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.2, random_state=42)

# 创建LightGBM数据集
train_data = lgb.Dataset(X_train, label=y_train)
test_data = lgb.Dataset(X_test, label=y_test, reference=train_data)

# 模型参数
params = {
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': ['mae', 'rmse'],
    'num_leaves': 31,
    'learning_rate': 0.05,
    'feature_fraction': 0.9,
    'verbose': -1
}

# 训练模型
gbm = lgb.train(params,
                train_data,
                num_boost_round=500,
                valid_sets=[train_data, test_data],
                callbacks=[lgb.early_stopping(stopping_rounds=20)])

# 评估
predictions = gbm.predict(X_test)
print(f"MAE: {mean_absolute_error(y_test, predictions)}")
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, predictions))}")


# 5. 博弈论调整层
class GameTheoryAdjuster:
    def __init__(self, base_model, n_competitors=3):
        self.base_model = base_model
        self.n_competitors = n_competitors

    def competitor_response(self, X, our_price_change):
        """模拟竞品对我们价格变化的反应"""
        # 这里简化处理，实际应用中可以使用单独的模型预测竞品反应
        response = np.random.uniform(-0.5, 0.5, (X.shape[0], self.n_competitors))
        return response * our_price_change.reshape(-1, 1)

    def payoff_function(self, price_adjustment, X, y_true):
        """收益函数(负的损失函数)"""
        X_temp = X.copy()
        X_temp['base_price'] += price_adjustment

        # 预测竞品反应
        comp_responses = self.competitor_response(X_temp, price_adjustment)
        for i in range(self.n_competitors):
            X_temp[f'comp_{i}_price'] += comp_responses[:, i]

        # 更新特征
        X_temp = create_features(X_temp)

        # 预测销量
        pred_sales = self.base_model.predict(X_temp[features])

        # 计算收益(这里简化使用销售总额，实际应考虑利润)
        revenue = (X_temp['base_price'] * pred_sales).mean()

        # 加入调整惩罚(避免过大调整)
        adjustment_penalty = 0.1 * np.mean(price_adjustment ** 2)

        return -(revenue - adjustment_penalty)  # 负值因为我们要最小化

    def find_optimal_adjustment(self, X, initial_guess=0):
        """找到最优价格调整"""
        res = minimize(
            fun=self.payoff_function,
            x0=np.full(X.shape[0], initial_guess),
            args=(X, None),
            method='L-BFGS-B',
            bounds=[(-10, 10)] * X.shape[0],  # 限制价格调整范围
            options={'maxiter': 50}
        )
        return res.x


# 使用博弈调整
adjuster = GameTheoryAdjuster(gbm)
optimal_adjustments = adjuster.find_optimal_adjustment(X_test)

# 应用调整
X_test_adj = X_test.copy()
X_test_adj['base_price'] += optimal_adjustments
X_test_adj = create_features(X_test_adj)

# 重新预测
adjusted_predictions = gbm.predict(X_test_adj[features])
print("\nAfter Game Theory Adjustment:")
print(f"MAE: {mean_absolute_error(y_test, adjusted_predictions)}")
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, adjusted_predictions))}")

# 6. 可视化结果
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 6))
plt.scatter(y_test, predictions, alpha=0.5, label='Original Predictions')
plt.scatter(y_test, adjusted_predictions, alpha=0.5, label='After Game Adjustment')
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'k--')
plt.xlabel('True Sales')
plt.ylabel('Predicted Sales')
plt.title('Prediction Comparison')
plt.legend()
plt.show()


# 实时更新
# 模型更新机制示例
def update_model(new_data, model):
    X_new = new_data[features]
    y_new = new_data[target]

    # 部分更新
    model = lgb.train(params,
                      train_data,
                      num_boost_round=50,
                      init_model=model,
                      valid_sets=[train_data, test_data],
                      callbacks=[lgb.early_stopping(stopping_rounds=10)])
    return model


# 场景模拟
def simulate_market_scenarios(model, base_scenario, n_scenarios=5):
    scenarios = []
    for _ in range(n_scenarios):
        scenario = base_scenario.copy()
        # 随机改变竞品价格
        for i in range(3):
            scenario[f'comp_{i}_price'] *= np.random.uniform(0.9, 1.1)
        # 计算最优应对
        adj = adjuster.find_optimal_adjustment(scenario)
        scenario['base_price'] += adj
        scenarios.append(scenario)
    return scenarios
